type StorageItem<T> = [T | null, number]

export class DataStorage {
  constructor(private ns: string, private storage = localStorage) {}

  private key(key: string) {
    return `${this.ns}:${key}`
  }

  /**
   * @param expiresIn 单位：秒
   */
  set<T>(key: string, value: T, expiresIn?: number): T | null {
    if (expiresIn !== undefined && expiresIn <= 0) throw new Error()
    key = this.key(key)
    const expiresAt =
      expiresIn === undefined ? 0 : Math.floor(Date.now() / 1000) + expiresIn
    const item: StorageItem<T | undefined> = [value, expiresAt]
    this.storage.setItem(key, JSON.stringify(item))
    return value
  }

  remove<T>(key: string): T | null {
    const v = this.get<T>(key)
    this.storage.removeItem(this.key(key))
    return v
  }

  get<T>(key: string): T | null {
    key = this.key(key)
    let item: StorageItem<T>
    const json = this.storage.getItem(key)
    if (!json) return null
    try {
      item = JSON.parse(json)
    } catch {
      this.storage.removeItem(key)
      return null
    }
    if (!item || !Array.isArray(item)) {
      this.storage.removeItem(key)
      return null
    }
    const v = item[0]
    const e = item[1]
    if (typeof e === 'number' && e > 0 && e < Math.floor(Date.now() / 1000)) {
      this.storage.removeItem(key)
      return null
    }
    return v
  }

  clear() {
    const prefix = this.key('')
    const keys = []
    const len = this.storage.length
    for (let i = 0; i < len; i++) {
      const key = this.storage.key(i)
      if (key && key.startsWith(prefix)) {
        keys.push(key)
      }
    }
    keys.forEach((key) => {
      this.storage.removeItem(key)
    })
  }
}
